package com.ruoyi.common.security.config;

import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
import com.ruoyi.common.core.utils.ServletUtils;
import com.ruoyi.common.core.utils.SpringUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.security.utils.LoginHelper;
import com.ruoyi.common.security.config.properties.SecurityProperties;
import com.ruoyi.common.security.handler.AllUrlHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 权限安全配置
 *
 * @author Lion Li
 */

@Slf4j
@AutoConfiguration
@EnableConfigurationProperties(SecurityProperties.class)
@RequiredArgsConstructor
public class SecurityConfig implements WebMvcConfigurer {

    private final SecurityProperties securityProperties;

    public static final String AuthHead = "login-type";

    /**
     * 注册 Sa-Token 路由拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册路由拦截器，自定义验证规则
        registry.addInterceptor(new SaInterceptor(handler -> {
                AllUrlHandler allUrlHandler = SpringUtils.getBean(AllUrlHandler.class);
                // 登录验证 -- 排除多个路径
                SaRouter
                    // 获取所有的
                    .match(allUrlHandler.getUrls())  // 拦截的 path 列表
                    .check((r) -> {
                        /*
                         * 产生的问题:
                         * 1允许混合登录，则需要对URL做好权限控制
                         * 2不允许混合登录，则需要在配置中指定允许登录的体系
                         *
                         * 如果两个体系，不同服务器，但redis服务器相同，默认的"login"体系任然可以跨端认证
                         * 除了以上方案，可以使用更改tokenName，防止不同的服务器账号体系登录,或者链接不同的redis的db库也可以。
                         */
                        Map<String, StpLogic> stpLogicMap = SaManager.stpLogicMap;
                        log.info("读取配置文件静态变量:" + SecurityProperties.loginType);
                        String loginType = StpUtil.TYPE;
                        if(StringUtils.isNotBlank(SecurityProperties.loginType)){
                            loginType=SecurityProperties.loginType;
                        }else{
                            if(StringUtils.isNotBlank(ServletUtils.getRequest().getHeader(AuthHead))){
                                loginType=ServletUtils.getRequest().getHeader(AuthHead);
                            }
                        }
                        StpLogic stpLogic = stpLogicMap.get(loginType);
                        boolean isLoin = false;
                        if(stpLogic.isLogin()){
                            // 检查 header 与 param 里的 clientid 与 token 里的是否一致(jwt)
//                            String headerCid = ServletUtils.getRequest().getHeader(LoginHelper.CLIENT_KEY);
//                            String paramCid = ServletUtils.getParameter(LoginHelper.CLIENT_KEY);
//                            String clientId = stpLogic.getExtra(LoginHelper.CLIENT_KEY).toString();
//                            if (!StringUtils.equalsAny(clientId, headerCid, paramCid)) {
//                                // token 无效
//                                throw NotLoginException.newInstance(StpUtil.getLoginType(),
//                                    "-100", "客户端ID与Token不匹配",
//                                    StpUtil.getTokenValue());
//                            }
                            isLoin = true;
                        }
                        if(!isLoin){
                            throw NotLoginException.newInstance("","-1","请登录后再访问",null);
                        }
                    });
            })).addPathPatterns("/**")
            // 排除不需要拦截的路径
            .excludePathPatterns(securityProperties.getExcludes());
    }
}
